#define _POSIX_SOURCE
#define _BSD_SOURCE

#include "dominion.h"
#include "dominion_helpers.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <math.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include "rngs.h"
#include <getopt.h>

#ifndef TEST_CASES
#define TEST_CASES 10000
#endif

#ifndef RAND_NUM
#define RAND_NUM 100
#endif

#ifndef SUPP
#define SUPP 10
#endif

int outputState(struct gameState *state, FILE *fp)
{
	int i, j;

	fprintf(fp, "****GAME STATE****\n");
	fprintf(fp, "Number of Players: %d\n", state->numPlayers);
	fprintf(fp, "Supply Count:\n");
	
	for(i = 0; i < treasure_map + 1; i++) {
		fprintf(fp, "	Card: %d Amount: %d\n", i, state->supplyCount[i]);
	}
	
	fprintf(fp, "Embargo Tokens\n");

	for(i = 0; i < treasure_map + 1; i++) {
		fprintf(fp, "	Card: %d Tokens: %d\n", i, state->embargoTokens[i]);
	}

	fprintf(fp, "Outpost Played Status: %d\n", state->outpostPlayed);
	fprintf(fp, "Outpost Turn: %d\n", state->outpostTurn);
	fprintf(fp, "Phase: %d\n", state->phase);
	fprintf(fp, "Actions: %d\n", state->numActions);
	fprintf(fp, "Coins: %d\n", state->coins);
	fprintf(fp, "Buys: %d\n", state->numBuys);
	fprintf(fp, "HANDS\n");

	for(i = 0; i < state->numPlayers; i++) {

		fprintf(fp, "	Player %d Hand Count: %d\n    Hand: ", i, state->handCount[i]);
		
		for(j = 0; j < state->handCount[i]; j++) {
			fprintf(fp, "%d, ", state->hand[i][j]);
		}
		fprintf(fp, "\n");
	}

	fprintf(fp, "DECKS\n");
	for(i = 0; i < state->numPlayers; i++) {
		fprintf(fp, "	Player %d Deck Count: %d\n    Deck: ", i, state->deckCount[i]);
		
		for(j = 0; j < state->deckCount[i]; j++) {
			fprintf(fp, "%d, ", state->deck[i][j]);
		}
		fprintf(fp, "\n");
	}

	fprintf(fp, "DISCARDS\n");
	for(i = 0; i < state->numPlayers; i++) {
		fprintf(fp, "	Player %d Discard Count: %d\n    Discard: ", i, state->discardCount[i]);
		
		for(j = 0; j < state->discardCount[i]; j++) {
			fprintf(fp, "%d, ", state->discard[i][j]);
		}
		fprintf(fp, "\n");
	}

	fprintf(fp, "Played Count: %d\n    Played Cards: ", state->playedCardCount);
	for(i = 0; i < state->playedCardCount; i++) {
		fprintf(fp, "%d, ", state->playedCards[i]);
	}
	fprintf(fp, "\n");

	return 0;
}

int createGameState(struct gameState *state, int card, int *handPos, int *choice1, int *choice2)
{
	int i, j;

	// Generate 2-4 players
	state->numPlayers = floor(Random() * (MAX_PLAYERS - 1)) + 2;
	state->whoseTurn = floor(Random() * state->numPlayers);

	state->numActions = floor(Random() * RAND_NUM);
	state->numBuys = floor(Random() * RAND_NUM);
	state->coins = floor(Random() * RAND_NUM);
	state->playedCardCount = floor(Random() * RAND_NUM);

	// Generate Supply Cards
	for (i = 0; i <= treasure_map; i++) {
		state->supplyCount[i] = floor(Random() * SUPP);
	}

	for (i = 0; i < state->numPlayers; i++) {
		state->handCount[i] = floor(Random() * RAND_NUM);
		state->deckCount[i] = floor(Random() * (RAND_NUM - 4)) + 5;
		state->discardCount[i] = floor(Random() * RAND_NUM);

		for (j = 0; j < state->handCount[i]; j++) {
			state->hand[i][j] = floor(Random() * (treasure_map + 1));
		}

		for (j = 0; j < state->deckCount[i]; j++) {
			state->deck[i][j] = floor(Random() * (treasure_map + 1));
		}

		for (j = 0; j < state->discardCount[i]; j++) {
			state->discard[i][j] = floor(Random() * (treasure_map + 1));
		}
	}

	// Make sure current player has the card to test
	if (state->handCount[state->whoseTurn] <= 0) {
		state->hand[state->whoseTurn][1] = card;
		state->hand[state->whoseTurn][0] = floor(Random() * (treasure_map + 1));
		state->handCount[state->whoseTurn] += 2;
		*handPos = 1;
		*choice1 = 0;
	} else if (state->handCount[state->whoseTurn] == 1) {
		state->hand[state->whoseTurn][0] = card;
		state->hand[state->whoseTurn][1] = floor(Random() * (treasure_map + 1));
		state->handCount[state->whoseTurn] += 1;
		*handPos = 0;
		*choice1 = 1;
	} else {
		*handPos = floor(Random() * state->handCount[state->whoseTurn]);
		state->hand[state->whoseTurn][*handPos] = card;

		//Choices are from the player's hand (OK to == handPos)
		*choice1 = floor(Random() * state->handCount[state->whoseTurn]);
	}
	
	// choice2 range from -1 to 3
	*choice2 = floor(Random() * 5) - 1;

	return 0;
}

int test_cardambassador(struct gameState *state, int handPos, int choice1, int choice2)
{
	int i, j, result, counter = 0, card;
	struct gameState pre;

	memcpy(&pre, state, sizeof(struct gameState));

	result = cardEffect(ambassador, choice1, choice2, -1, state, handPos, NULL);

	card = pre.hand[pre.whoseTurn][choice1];

	// Handle an error result
	if (result == -1) {
		// choice2 must be between 0 and 2
		if (choice2 < 0 || choice2 > 2) {
			return 0;
		}

		// choice1 cannot equal handPos
		if (choice1 == handPos) {
			return 0;
		}

		// there must be enough cards for choice2
		for (i = 0; i < state->handCount[state->whoseTurn]; i++) {
			if (i != handPos && state->hand[state->whoseTurn][i] == card) {
				counter += 1;
			}
		}
		if (counter < choice2) {
			return 0;
		}

		return -1;
	}

	// SET UP EXPECTED CONDITIONS HERE
	pre.supplyCount[card] += choice2;
	
	for (i = 0; i < pre.numPlayers; i++) {
		if (i == pre.whoseTurn) {
			continue;
		} else {
			if (pre.supplyCount[card] > 0) {
				pre.discard[i][pre.discardCount[i]] = card;
				pre.discardCount[i] += 1;
				pre.supplyCount[card] -= 1;
			}
		}
	}

	// Find and toss the played card
	discardCard(handPos, pre.whoseTurn, &pre, 0);

	// Toss up to choice2 cards of that card type
	for (i = 0; i < choice2; i++) {
		for (j = 0; j < pre.handCount[pre.whoseTurn]; j++) {
			if (pre.hand[pre.whoseTurn][j] == card) {
				discardCard(j, pre.whoseTurn, &pre, 1);
				break;
			}
		}
	}

	if (memcmp(&pre, state, sizeof(struct gameState)) != 0) {
		printf("\t# WTF IS THIS\n");
		outputState(&pre, stderr);
		return -1;
	}

	return 0;
}

int main(int argc, char **argv)
{
	int i, result, handPos, choice1, choice2, counter = 0;
	struct gameState state;

	for (i = 0; i < TEST_CASES; i++) {

		// Reset variables
		handPos = 0;
		choice1 = 0;
		choice2 = 0;
		
		createGameState(&state, ambassador, &handPos, &choice1, &choice2);
		result = test_cardambassador(&state, handPos, choice1, choice2);
		if (result == 0) {
			printf("TEST CASE #%d = PASS\n", i + 1);
			counter += 1;
		} else {
			printf("TEST CASE #%d = FAIL <------\n", i + 1);
			outputState(&state, stderr);
		}
	}

	printf("PASSED CASES: %d out of %d\n", counter, TEST_CASES);
	return 0;
}